创建DownloadHandlers
有几种类型的DownloadHandlers
:
• DownloadHandlerBuffer 用于简单的数据存储。
• DownloadHandlerFile 用于下载文件并将其保存到内存较少的磁盘。
• DownloadHandlerTexture 用于下载图像。
• DownloadHandlerAssetBundle 用于提取AssetBundles。
• DownloadHandlerAudioClip 用于下载音频文件。
• DownloadHandlerMovieTexture 用于下载视频文件。
•DownloadHandlerScript是一个特殊的班级。就其本身而言,它什么都不做。但是,该类可以由用户定义的类继承。该类接收来自UnityWebRequest系统的回调,然后可以用它来完成从网络到达的数据的完全自定义处理。
这些API与DownloadHandlerTexture
接口类似。
UnityWebRequest
有一个属性disposeDownloadHandlerOnDispose
,默认为true
。如果此属性为true
,则在处置UnityWebRequest
对象时,Dispose()
也将在附加的下载处理程序上调用,从而使其无效。如果您保留对下载处理程序的引用超过对UnityWebRequest
的引用,则应将disposeDownloadHandlerOnDispose
设置为false
。
DownloadHandlerBuffer
这个下载处理程序是最简单的,并处理大部分用例。它将接收到的数据存储在本机代码缓冲区中。下载完成后,您可以以字节数组或字符串的形式访问缓冲的数据。
例
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class MyBehaviour : MonoBehaviour {
void Start() {
StartCoroutine(GetText());
}
IEnumerator GetText() {
UnityWebRequest www = new UnityWebRequest("http://www.my-server.com");
www.downloadHandler = new DownloadHandlerBuffer();
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
// Show results as text
Debug.Log(www.downloadHandler.text);
// Or retrieve results as binary data
byte[] results = www.downloadHandler.data;
}
}
}
DownloadHandlerFile
这是一个特殊的大文件下载处理程序。它将下载的字节直接写入文件,因此无论正在下载的文件的大小如何,内存使用率都很低。与其他下载处理程序的区别是,您无法从中获取数据,所有数据都保存到文件中。
例
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
public class FileDownloader : MonoBehaviour {
void Start () {
StartCoroutine(DownloadFile());
}
IEnumerator DownloadFile() {
var uwr = new UnityWebRequest("https://unity3d.com/", UnityWebRequest.kHttpVerbGET);
string path = Path.Combine(Application.persistentDataPath, "unity3d.html");
uwr.downloadHandler = new DownloadHandlerFile(path);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
Debug.LogError(uwr.error);
else
Debug.Log("File successfully downloaded and saved to " + path);
}
}
DownloadHandlerTexture
不使用DownloadHandlerBuffer
来下载图像文件,然后使用原始字节创建纹理Texture.LoadImage
,而是使用效率更高DownloadHandlerTexture
。
该下载处理程序将收到的数据存储在UnityEngine.Texture
。完成下载后,它会将JPEG和PNG解码为有效UnityEngine.Texture
objects
。UnityEngine.Texture
每个DownloadHandlerTexture
对象只创建一个副本。这减少了垃圾收集的性能命中。该处理程序在本地代码中执行缓冲,解压缩和纹理创建。此外,解压缩和纹理创建是在工作线程而不是主线程上执行的,这可以在加载大纹理时提高帧时间。
最后,DownloadHandlerTexture
在最终创建Texture
本身时只分配托管内存,这消除了与在脚本中执行字节到纹理转换相关的垃圾回收开销。
例
以下示例从互联网下载PNG文件,将其转换为Sprite
并将其分配给图像:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;
[RequireComponent(typeof(UnityEngine.UI.Image))]
public class ImageDownloader : MonoBehaviour {
UnityEngine.UI.Image _img;
void Start () {
_img = GetComponent<UnityEngine.UI.Image>();
Download("http://www.mysite.com/myimage.png");
}
public void Download(string url) {
StartCoroutine(LoadFromWeb(url));
}
IEnumerator LoadFromWeb(string url)
{
UnityWebRequest wr = new UnityWebRequest(url);
DownloadHandlerTexture texDl = new DownloadHandlerTexture(true);
wr.downloadHandler = texDl;
yield return wr.SendWebRequest();
if(!(wr.isNetworkError || wr.isHttpError)) {
Texture2D t = texDl.texture;
Sprite s = Sprite.Create(t, new Rect(0, 0, t.width, t.height),
Vector2.zero, 1f);
_img.sprite = s;
}
}
}
DownloadHandlerAssetBundle
这个专门的下载处理程序的好处是它能够将数据流式传输到Unity的AssetBundle
系统。一旦AssetBundle
系统收到足够的数据,AssetBundle
就可以作为一个UnityEngine.AssetBundle
对象使用。只UnityEngine.AssetBundle
创建一个对象的副本。这大大减少了运行时内存分配以及加载AssetBundle
时的内存影响。它还允许AssetBundles
在未完全下载的情况下部分使用,因此您可以对资产进行流式处理。
所有下载和解压缩都发生在工作线程上。
AssetBundles
通过一个DownloadHandlerAssetBundle
对象下载,该对象有一个特殊的assetBundle
属性来检索AssetBundle
。
由于AssetBundle
系统的工作方式,所有AssetBundle
必须有一个与它们相关的地址。通常,这是它们所在的名义URL(意味着任何重定向之前的URL)。几乎在所有情况下,您应该传递给您传递给UnityWebRequest
的相同URL。使用高级API(HLAPI)时,这是为您完成的。
例
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class MyBehaviour : MonoBehaviour {
void Start() {
StartCoroutine(GetAssetBundle());
}
IEnumerator GetAssetBundle() {
UnityWebRequest www = new UnityWebRequest("http://www.my-server.com");
DownloadHandlerAssetBundle handler = new DownloadHandlerAssetBundle(www.url, uint.MaxValue);
www.downloadHandler = handler;
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
// Extracts AssetBundle
AssetBundle bundle = handler.assetBundle;
}
}
}
DownloadHandlerAudioClip
该下载处理程序经过优化,可用于下载音频文件。您可以使用此下载处理程序以更方便的方式执行,而不是使用下载原始字节DownloadHandlerBuffer
然后创建AudioClip
它们。
例
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class AudioDownloader : MonoBehaviour {
void Start () {
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip() {
using (var uwr = UnityWebRequestMultimedia.GetAudioClip("http://myserver.com/mysound.ogg", AudioType.OGGVORBIS)) {
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError) {
Debug.LogError(uwr.error);
yield break;
}
AudioClip clip = DownloadHandlerAudioClip.GetContent(uwr);
// use audio clip
}
}
}
DownloadHandlerMovieTexture
该下载处理程序经过优化以下载视频文件。您可以使用此下载处理程序以更方便的方式执行,而不是使用下载原始字节DownloadHandlerBuffer
然后创建MovieTexture
它们。
例
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class MovieDownloader : MonoBehaviour {
void Start () {
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip() {
using (var uwr = UnityWebRequestMultimedia.GetMovieTexture("http://myserver.com/mysound.ogg")) {
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError) {
Debug.LogError(uwr.error);
yield break;
}
MovieTexture movie = DownloadHandlerMovieTexture.GetContent(uwr);
// use movie texture
}
}
}
DownloadHandlerScript
对于需要完全控制下载数据处理的用户,Unity提供DownloadHandlerScript
该类。
默认情况下,这个类的实例什么都不做。但是,如果您从中派生自己的类DownloadHandlerScript,则可能会覆盖某些函数,并在数据从网络到达时使用它们来接收回调。
注意:实际下载发生在工作线程上,但所有DownloadHandlerScript
回调都在主线程上运行。避免在这些回调期间执行计算量大的操作。
覆盖的功能 ReceiveContentLength()
protected void ReceiveContentLength(long contentLength);
这个函数在收到Content-Length头时被调用。请注意,如果您的服务器在处理UnityWebRequest的过程中发送一个或多个重定向响应,则可能会多次发生此回调。
OnContentComplete()
protected void OnContentComplete();
当UnityWebRequest从服务器完全下载所有数据并将所有接收到的数据转发给ReceiveData回调时,将调用此函数。
receiveData()
protected bool ReceiveData(byte[] data, long dataLength);
该数据从远程服务器到达后调用,每帧调用一次。该data
参数包含从远程服务器接收到的原始字节,并dataLength
指示数据数组中新数据的长度。
当不使用预先分配的数据缓冲区时,系统每次调用此回调时都会创建一个新的字节数组,并且dataLength
始终等于data.Length
。使用预先分配的数据缓冲区时,数据缓冲区将被重用,并且dataLength
必须用于查找更新的字节数。
该函数需要返回值为true
或false
。如果您返回false
,系统将立即中止UnityWebRequest
。如果返回true
,则处理正常继续。
避免垃圾收集开销
Unity的许多更高级用户都关心减少垃圾回收造成的CPU峰值。对于这些用户,UnityWebRequest
系统允许预先分配托管代码字节数组,该数组用于将下载的数据传递给DownloadHandlerScript
的ReceiveData
回调函数。
在使用DownloadHandlerScript
派生类捕获下载的数据时,使用此函数可以完全消除托管代码内存分配。
要DownloadHandlerScript
使用预先分配的托管缓冲区进行操作,请向其构造函数提供一个字节数组DownloadHandlerScript
。
注意:字节数组的大小限制了每帧传送给ReceiveData
回调的数据量。如果数据缓慢到达,在很多帧中,您可能提供了太小的字节数组。
例
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class LoggingDownloadHandler : DownloadHandlerScript {
// Standard scripted download handler - allocates memory on each ReceiveData callback
public LoggingDownloadHandler(): base() {
}
// Pre-allocated scripted download handler
// reuses the supplied byte array to deliver data.
// Eliminates memory allocation.
public LoggingDownloadHandler(byte[] buffer): base(buffer) {
}
// Required by DownloadHandler base class. Called when you address the 'bytes' property.
protected override byte[] GetData() { return null; }
// Called once per frame when data has been received from the network.
protected override bool ReceiveData(byte[] data, int dataLength) {
if(data == null || data.Length < 1) {
Debug.Log("LoggingDownloadHandler :: ReceiveData - received a null/empty buffer");
return false;
}
Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveData - received {0} bytes", dataLength));
return true;
}
// Called when all data has been received from the server and delivered via ReceiveData.
protected override void CompleteContent() {
Debug.Log("LoggingDownloadHandler :: CompleteContent - DOWNLOAD COMPLETE!");
}
// Called when a Content-Length header is received from the server.
protected override void ReceiveContentLength(int contentLength) {
Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveContentLength - length {0}", contentLength));
}
}
🔚